/**************************************************************************
*
* Tint Task Manager
* 
* Copyright (C) 2007 Pål Staurland (staura@gmail.com)
* 
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Soft-
* ware"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, subject to the following condi-
* tions:
* 
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
* ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* 
**************************************************************************/

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "task.h"
#include "window.h"
#include "server.h"
#include "config.h"

void set_active (Window win)
{
	XEvent event;
	long mask = SubstructureRedirectMask|SubstructureNotifyMask;

	event.xclient.type = ClientMessage;
	event.xclient.serial = 0;
	event.xclient.send_event = True;
	event.xclient.display = server.dsp;
	event.xclient.window = win;
	event.xclient.message_type = server.atom._NET_ACTIVE_WINDOW;

	event.xclient.format = 32;
	event.xclient.data.l[0] = 2;
	event.xclient.data.l[1] = 0;
	event.xclient.data.l[2] = 0;
	event.xclient.data.l[3] = 0;
	event.xclient.data.l[4] = 0;

	XSendEvent(server.dsp, server.root_win, False, mask, &event);
}

int window_is_hidden (Window win)
{
	Atom *at; 
	int count, i;
	at = server_get_property (win, server.atom._NET_WM_WINDOW_TYPE, XA_ATOM, &count);
	
	for (i = 0; i < count; i++)
	{
		if (at[i] == server.atom._NET_WM_SKIP_PAGER || 
			at[i] == server.atom._NET_WM_STATE_SKIP_TASKBAR)
		{
			XFree(at);
			return 1;
		}
		
	}
	XFree(at);

	at = server_get_property (win, server.atom._NET_WM_WINDOW_TYPE, XA_ATOM, &count);
	
	for (i = 0; i < count; i++)
	{
		if (	at[i] == server.atom._NET_WM_WINDOW_TYPE_DOCK ||
				at[i] == server.atom._NET_WM_WINDOW_TYPE_DESKTOP ||
				at[i] == server.atom._NET_WM_WINDOW_TYPE_TOOLBAR ||
				at[i] == server.atom._NET_WM_WINDOW_TYPE_MENU ||
				at[i] == server.atom._NET_WM_WINDOW_TYPE_SPLASH)
		{
			XFree(at);
			return 1;
		}	
	}
	XFree(at);
	return 0;
}

void window_make_active (Window win)
{
	XEvent event;
	long mask = SubstructureRedirectMask|SubstructureNotifyMask;

	event.xclient.type = ClientMessage;
	event.xclient.serial = 0;
	event.xclient.send_event = True;
	event.xclient.display = server.dsp;
	event.xclient.window = win;
	event.xclient.message_type = server.atom._NET_ACTIVE_WINDOW;

	event.xclient.format = 32;
	event.xclient.data.l[0] = 2;
	event.xclient.data.l[1] = 0;
	event.xclient.data.l[2] = 0;
	event.xclient.data.l[3] = 0;
	event.xclient.data.l[4] = 0;

	XSendEvent(server.dsp, server.root_win, False, mask, &event);
}

int window_get_desktop (Window win)
{
	int desk;
	unsigned long *res;
	
	desk = 0;
	
	res = server_get_property (win, server.atom._NET_WM_DESKTOP, XA_CARDINAL, 0);
	
	if (res)
	{
		desk = res[0];
		XFree (res);
	}
	return desk;
}	

Window window_get_active ()
{
	Window *wins, win;
	
	wins = server_get_property (server.root_win, server.atom._NET_ACTIVE_WINDOW, XA_WINDOW, 0);
	if (wins)
	{
		win = *wins;
		XFree (wins);
		return win;
	}
	else
		return 0;
}

int window_is_active (Window win)
{
	Window *wins;
	int act;
	
	act = 0;
	wins = server_get_property (server.root_win, server.atom._NET_ACTIVE_WINDOW, XA_WINDOW, 0);
	
	if (wins)
	{
		act = (win == wins[0]);
		XFree(wins);
	}
	
	return act;
}

int window_is_iconified (Window win)
{
	unsigned long *res;
	int ico;
	
	ico = 0;
	
	res = server_get_property (win, server.atom.WM_STATE, server.atom.WM_STATE, 0);
	if (res)
	{
		ico = (res[0] == IconicState);
		XFree(res);
	}
	
	return ico;
}

void window_toggle_iconify (Window win)
{
	Task *tsk;
	tsk = task_get_task (win);

	if (tsk->iconified)
	{
		XMapWindow (server.dsp, win);
		if (!tsk->active)
			set_active(tsk->win);
	}
	else 
	{
		if (tsk->active)
			XIconifyWindow (server.dsp, win, server.screen);
		else 
			set_active (tsk->win);
	}
}

void window_toggle_shade (Window win)
{
	XEvent event;
	long mask = SubstructureRedirectMask|SubstructureNotifyMask;

	event.xclient.type = ClientMessage;
	event.xclient.serial = 0;
	event.xclient.send_event = True;
	event.xclient.display = server.dsp;
	event.xclient.window = win;
	event.xclient.message_type = server.atom._NET_WM_STATE;

	event.xclient.format = 32;
	event.xclient.data.l[0] = 2;
	event.xclient.data.l[1] = server.atom._NET_WM_STATE_SHADED;
	event.xclient.data.l[2] = 0;
	event.xclient.data.l[3] = 0;
	event.xclient.data.l[4] = 0;

	XSendEvent(server.dsp, server.root_win, False, mask, &event);	
}

int get_icon_count (long *data, int num)
{
	int count, pos, w, h;
	
	count = 0;
	pos = 0;
	while (pos < num) 
	{
			w = data[pos++];
			h = data[pos++];
			pos += w * h;
			if (pos > num || w * h == 0)
				break;
			count++;
	} 	
	
	return count;
}

long *get_best_icon (long *data, int icon_count, int num, int *iw, int *ih)
{
 	int width[icon_count], height[icon_count], pos, i, w, h;
	long *icon_data[icon_count];
	
	/* List up icons */
	pos = 0;
	i = icon_count;
	while (i--)
	{
		w = data[pos++];
		h = data[pos++];	
		if (pos + w * h > num)
			break;
		
		width[i] = w;
		height[i] = h;
		icon_data[i] = &data[pos];		

		pos += w * h;
	}

	/* Try to find exact size */
	int icon_num = -1;
	for (i = 0; i < icon_count; i++)
	{
		if (width[i] == config.icon_size)
		{
			icon_num = i;
			break;
		}
	}
			
	/* Take the biggest or whatever */			
	if (icon_num < 0)
	{
		int highest = 0;
		for (i = 0; i < icon_count; i++)
		{
				if (width[i] > highest)
				{
					icon_num = i;
					highest = width[i];
					
				}
		}
	}	
	
	*iw = width[icon_num];
	*ih = height[icon_num];
	return icon_data[icon_num];
}

void window_get_icon (Task *tsk)
{
	long *data;
	int num;
	
	if (tsk->icon_data != 0)
		return;
	
	data = server_get_property (tsk->win, server.atom._NET_WM_ICON, XA_CARDINAL, &num);
	
	if (!data)
		return;
	
	int w, h;
	long *tmp_data;
	
	tmp_data = get_best_icon (data, get_icon_count (data, num), num, &w, &h);
	
	tsk->icon_data = malloc (w * h * sizeof (long));
    memcpy (tsk->icon_data, tmp_data, w * h * sizeof (long));
	
	tsk->icon_width = w;
	tsk->icon_height = h;
	
	XFree (data);
}


void set_panel_properties (Window win)
{
	
	XSizeHints size_hints;
	XWMHints wmhints;
	long val;
	
	XStoreName (server.dsp, win, "tint");
	
	/* Dock */
	val = server.atom._NET_WM_WINDOW_TYPE_DOCK;
	XChangeProperty (	server.dsp, win, server.atom._NET_WM_WINDOW_TYPE, XA_ATOM, 
						32, PropModeReplace, (unsigned char *) &val, 1);
   	
   	/* Sticky */
   	val = 0xFFFFFFFF;
	XChangeProperty (	server.dsp, win, server.atom._NET_WM_DESKTOP, XA_CARDINAL, 
						32, PropModeReplace, (unsigned char *) &val, 1);
   	
   	/* Fixed position */
	size_hints.flags = PPosition;
	XChangeProperty (	server.dsp, win, XA_WM_NORMAL_HINTS, XA_WM_SIZE_HINTS, 32, PropModeReplace, 
						(unsigned char *) &size_hints, sizeof (XSizeHints) / 4);
	
	/* Unfocusable */
	wmhints.flags = InputHint;
	wmhints.input = False;
	XChangeProperty (	server.dsp, win, XA_WM_HINTS, XA_WM_HINTS, 32, PropModeReplace,
						(unsigned char *) &wmhints, sizeof (XWMHints) / 4);	
}

void lower_panel ()
{
	XClientMessageEvent xev;
	
	xev.type = ClientMessage;
	xev.window = window.main_win;
	xev.message_type = server.atom._NET_WM_STATE;
	xev.format = 32;
	xev.data.l[0] = 1;	
	xev.data.l[1] = server.atom._NET_WM_STATE_BELOW;
	xev.data.l[2] = 0;
	XSendEvent (server.dsp, server.root_win, False, SubstructureNotifyMask, (XEvent *) &xev);
	XLowerWindow (server.dsp, window.main_win);		
}

void window_draw_panel ()
{
	Window win;
	XSetWindowAttributes att;

	server.x = (server.screen_width / 2) - (config.panel_width / 2);
	server.y = server.screen_height - config.panel_height;
	
	/* Catch some events */
	att.event_mask = ExposureMask|ButtonPressMask|StructureNotifyMask;

	win = XCreateWindow (  	/* display 	*/ server.dsp,
							/* parent  	*/ server.root_win,
							/* x       	*/ server.x,
							/* y       	*/ server.y,
							/* width   	*/ config.panel_width,
							/* height  	*/ config.panel_height,
							/* border  	*/ 0,
							/* depth   	*/ server.depth,
							/* class   	*/ InputOutput,
							/* visual	*/ CopyFromParent,
							/* mask 	*/ CWEventMask,
							/* attribs 	*/ &att);
            
	set_panel_properties (win);
	window.main_win = win;
	
	XMapWindow (server.dsp, win);
	XFlush (server.dsp);
	
	lower_panel ();
}
